﻿@model protogen.site.Controllers.HomeController.DecodeModel
@functions {
    long Zag(long value)
    {
        const long Int64Msb = ((long)1) << 63;
        return (-(value & 0x01L)) ^ ((value >> 1) & ~Int64Msb);
    }
    double ToDouble(long val) => BitConverter.ToDouble(BitConverter.GetBytes(val), 0);
    double ToSingle(int val) => BitConverter.ToSingle(BitConverter.GetBytes(val), 0);
}
@{
    Layout = "";
}
@try
{
    ProtoBuf.SubItemToken tok = default(ProtoBuf.SubItemToken);
    using(var reader = Model.GetReader())
    {
        int field, start = 0;
        while((field = reader.ReadFieldHeader()) > 0)
        {
            @if(start == 0 && field == Model.SkipField && reader.WireType == ProtoBuf.WireType.StartGroup)
            {
                tok = ProtoBuf.ProtoReader.StartSubItem(reader);
                start = reader.Position;
                continue;
            }
            @if(start != 0)
            {
                <hr/>
            }
            int headerEnd = reader.Position;
                    
            <p data-offset="@start">@Model.AsHex(start, headerEnd - start) = field <b>@(field)</b>, type <b>@(reader.WireType)</b></p>
            switch(reader.WireType)
            {
                case ProtoBuf.WireType.Variant:
                    var val = reader.ReadInt64();
                    <p>@Model.AsHex(headerEnd, reader.Position - headerEnd) = <b>@(val)</b> (raw) or <b>@(Zag(val))</b> (zigzag)</p>
                    break;
                case ProtoBuf.WireType.StartGroup:
                    reader.SkipField();
                    <p>payload (inc. terminator) = @Model.AsHex(headerEnd, reader.Position - headerEnd)</p>
                    if(Model.Deep)
                    {
                        var slice = Model.Slice(start, reader.Position - start, field);
                        <div style="padding-left: 2em">
                            @Html.Partial("DecodeBytes", slice)
                        </div>
                    }
                    else
                    {
                        <form method="post"><input type="hidden" name="hex" value="@(Model.AsHex(headerEnd, reader.Position - headerEnd))" /><input type="submit" value="decode" /></form>
                    }
                    break;
                case ProtoBuf.WireType.String:
                    {
                    var payloadBytes = ProtoBuf.ProtoReader.AppendBytes(null, reader);
                    var lenLen = reader.Position - payloadBytes.Length - headerEnd;
                    <p>@Model.AsHex(headerEnd, lenLen) = length <b>@(payloadBytes.Length)</b></p>
                    var slice = new protogen.site.Controllers.HomeController.DecodeModel(payloadBytes, Model.Deep);
                    if(slice.Count == 0)
                    {
                        <p>(empty payload)</p>
                    }
                    else if(slice.Count > 256)
                    {
                        <p>(@slice.Count bytes)</p>
                    }
                    else
                    {
                    <p>payload = @slice.AsHex()</p>
                    var s = slice.AsString();
                    if(!string.IsNullOrEmpty(s))
                    {
                    <p>UTF8: @s</p>
                    }
                    }
                    if(slice.Count == 0)
                    {}
                    else if(slice.CouldBeProto())
                    {
                        if(Model.Deep)
                        {
                        <div style="padding-left: 2em">
                        @Html.Partial("DecodeBytes", slice)
                        </div>
                        }
                        else
                        {
                        <form method="post"><input type="hidden" name="hex" value="@(slice.AsHex())" /><input type="submit" value="decode" /></form>
                        }
                    }
                    }
                    break;
                case ProtoBuf.WireType.Fixed64:
                    var val64 = reader.ReadInt64();
                    <p>@Model.AsHex(headerEnd, reader.Position - headerEnd) = <b>@(val64)</b> (integer) or <b>@(ToDouble(val64))</b> (floating point)</p>
                    break;
                case ProtoBuf.WireType.Fixed32:
                    var val32 = reader.ReadInt32();
                    <p>@Model.AsHex(headerEnd, reader.Position - headerEnd) = <b>@(val32)</b> (integer) or <b>@(ToSingle(val32))</b> (floating point)</p>
                    break;
                case ProtoBuf.WireType.EndGroup:
                    break;
                default:
                    <p>unexpected wire-type: @(reader.WireType)</p>
                    break;
            }
            start = reader.Position;
        }
        @if(Model.SkipField > 0 && start != 0)
        {
            ProtoBuf.ProtoReader.EndSubItem(tok, reader);
            int suffixLen = reader.Position - start;
            if(suffixLen != 0)
            {
              <hr />
              <p>@Model.AsHex(start, suffixLen) = field <b>@(Model.SkipField)</b>, type <b>@(ProtoBuf.WireType.EndGroup)</b></p>
            }
        }
        @if(reader.Position < Model.Count)
        {
        <p>remaining data: @Model.AsHex(reader.Position, Model.Count - reader.Position)</p>
        <form method="post"><input type="hidden" name="hex" value="@(Model.AsHex(reader.Position, Model.Count - reader.Position))"/><input type="submit" value="decode"/></form>
        }
    }
}
catch(Exception ex)
{
<p>error: @(ex.Message)</p>
}